/*********************************************************************
* 版权所有(C)   凌聪
* 项目代号:     代码整理
* 文件名称:     UdpSocket.cpp
* 运行平台:     Linux
* 编译环境:     C++编译器--G++
* 程序语言:     c++
* 作   者：     凌聪
* 内容摘要:     UdpSock相关操作
* 开始日期:     2020年02月20日
***********************************************************************/

#include <string.h>

#include "UdpSocket.h"

/**
 * 客户端，可任意端口号
 **/
UdpSocket::UdpSocket() 
{
    sockFd = socket(AF_INET, SOCK_DGRAM, 0);
    
    if(sockFd < 0){
        D("socket create fail: %s\n", strerror(errno));
        return;
    }
}

/**
 * 只绑定本地端口号
 **/
UdpSocket::UdpSocket(int port) 
{
    sockFd = socket(AF_INET, SOCK_DGRAM, 0);
    
    if(sockFd < 0){
        D("socket create fail: %s\n", strerror(errno));
        return;
    }
    
    struct sockaddr_in sockAddr;
    memset(&sockAddr, 0, sizeof(sockAddr));
    sockAddr.sin_family = AF_INET;
    sockAddr.sin_port = htons(port);
    sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    
    //绑定本地端口
    int res = bind(sockFd, (struct sockaddr*)&sockAddr, sizeof(sockAddr));
    if(res < 0) {
        D("CommUdpServer=>bind is error: %s\n", strerror(errno));
        close(sockFd);
        sockFd = -1;
        return;
    }
    
    memset(&remoteSock, 0, sizeof(remoteSock));
    localPort = port;
}

/**
 * 绑定本地端口号和本地ip地址
 **/
UdpSocket::UdpSocket(char *ipAddr, int port)
{
    sockFd = socket(AF_INET, SOCK_DGRAM, 0);
    
    if(sockFd < 0){
        D("socket create fail: %s\n", strerror(errno));
        return;
    }
    
    struct sockaddr_in sockAddr;
    memset(&sockAddr, 0, sizeof(sockAddr));
    sockAddr.sin_family = AF_INET;
    sockAddr.sin_port = htons(port);
    sockAddr.sin_addr.s_addr = inet_addr(ipAddr);
    
    //绑定本地端口
    int res = bind(sockFd, (struct sockaddr*)&sockAddr, sizeof(sockAddr));
    if(res < 0) {
        D("CommUdpServer=>bind is error: %s\n", strerror(errno));
        close(sockFd);
        sockFd = -1;
        return;
    }
    
    memset(&remoteSock, 0, sizeof(remoteSock));
    localPort = port;
}

UdpSocket::UdpSocket(const UdpSocket& orig)
{
    
}

UdpSocket::~UdpSocket() 
{
    if(sockFd > 0){
        close(sockFd);
    }
}

/**
 * 设置至指定target，此target不会随recv接收到的地址和端口而变化
 * 与send2Target配套使用
 **/
void UdpSocket::setRemoteTarget(char *ipAddr, int port)
{
    memset(&remoteSock, 0, sizeof(remoteSock));
    remoteSock.sin_family = AF_INET;
    remoteSock.sin_addr.s_addr =  inet_addr(ipAddr);
    remoteSock.sin_port = htons(port);
}

/**
 * 发送至指定target，此target不会随recv接收到的地址和端口而变化
 **/
int UdpSocket::send2Target(char *sendBuf, int length)
{
    int ret;
    
    struct sockaddr_in sockClient;
    memset(&sockClient, 0, sizeof(sockClient));
    
    if(memcmp(&remoteSock, &sockClient, sizeof(sockClient)) == 0){
        D("remote target hasn't set\r\n");
        return 0;
    }
    
    ret = sendto(sockFd, sendBuf, length, 0, (struct sockaddr*)&remoteSock, sizeof(struct sockaddr));
    
    return ret;
}

/**
 * UDP发送方法
 **/
int UdpSocket::send(char *dstIpAddr, int dstPort, char *sendBuf, int length)
{
    int ret;
    
    struct sockaddr_in sockClient;
    memset(&sockClient, 0, sizeof(sockClient));
    sockClient.sin_family = AF_INET;
    sockClient.sin_addr.s_addr =  inet_addr(dstIpAddr);
    sockClient.sin_port = htons(dstPort);
    ret = sendto(sockFd, sendBuf, length, 0, (struct sockaddr*)&sockClient, sizeof(sockaddr));
    
    return ret;
}

/**
 * 回复最新收到的udp的信息
 * win网口调试助手->ubuntu 一次最多发4096字节
 **/
int UdpSocket::send2Recv(char *sendBuf, int length)
{
    int ret;
    
    struct sockaddr_in sockClient;
    memset(&sockClient, 0, sizeof(sockClient));
    
    if(memcmp(&recvSock, &sockClient, sizeof(sockClient)) == 0){
        D("there is not recv target\r\n");
        return 0;
    }
    
    ret = sendto(sockFd, sendBuf, length, 0, (struct sockaddr*)&recvSock, sizeof(sockaddr));
    
    return ret;
}

/**
 * UDP接收方法
 **/
int UdpSocket::recv(char *recvBuf, int length)
{
    int ret;
    socklen_t sockLen = sizeof(struct sockaddr);
    
    ret = recvfrom(sockFd, recvBuf, length, 0, (struct sockaddr*)&recvSock, &sockLen);
    
    return ret;
}

void UdpSocket::setTimeout(unsigned int s, unsigned int us)
{
    setRecvTimeout(s, us);
    setSendTimeout(s, us);
}

/**
 * 设置接收超时
 **/
void UdpSocket::setRecvTimeout(unsigned int s, unsigned int us)
{
    if(sockFd < 0){
        D("%s sockFd = %d \r\n", __FUNCTION__, sockFd);
        return;
    }
    
    struct timeval tv;
    tv.tv_sec = s;
    tv.tv_usec = us;
    if (setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof (tv)) < 0) 
    {
        D("set SO_RCVTIMEO error:");
        return;
    }
    
}

/**
 * 设置发送超时
 **/
void UdpSocket::setSendTimeout(unsigned int s, unsigned int us)
{
    if(sockFd < 0){
        D("%s sockFd = %d \r\n", __FUNCTION__, sockFd);
        return;
    }
    
    struct timeval tv;
    tv.tv_sec = s;
    tv.tv_usec = us;
    if (setsockopt(sockFd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof (tv)) < 0) 
    {
        D("set SO_SNDTIMEO error:");
        return;
    }
    
}

/**
 * 获取本机ip,返回以“172.0.0.0”的形式
 **/
int UdpSocket::getLocalIp(char *eth_inf, char *ip)
{
    int sd;
    struct sockaddr_in sin;
    struct ifreq ifr;

    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == sd)
    {
        D("socket error: %s\n", strerror(errno));
        return -1;
    }

    strncpy(ifr.ifr_name, eth_inf, IFNAMSIZ);
    ifr.ifr_name[IFNAMSIZ - 1] = 0;

    // if error: No such device
    if (ioctl(sd, SIOCGIFADDR, &ifr) < 0)
    {
        D("ioctl error: %s\n", strerror(errno));
        close(sd);
        return -1;
    }

    memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
    snprintf(ip, MAX_IP_SIZE, "%s", inet_ntoa(sin.sin_addr));

    close(sd);
    return 0;
}

int UdpSocket::getLocalMac(char *eth_inf, char *mac)
{
    struct ifreq ifreq;
    int sock = 0;

    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock < 0)
    {
        D("error sock");
        return -2;
    }

    strcpy(ifreq.ifr_name, eth_inf);
    if(ioctl(sock, SIOCGIFHWADDR, &ifreq) < 0)
    {
        D("error ioctl");
        return -3;
    }

    int i = 0;
    for(i = 0; i < 6; i++){
        sprintf(mac+3*i, "%02X:", (unsigned char)ifreq.ifr_hwaddr.sa_data[i]);
    }
    mac[strlen(mac) - 1] = 0;
    D("MAC: %s\n", mac);

    close(sock);
}


